package com.market.web.controller;

import java.util.List;

import javax.faces.application.ConfigurableNavigationHandler;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.primefaces.event.SelectEvent;

import com.market.domain.BaseObject;
import com.market.manager.GenericManager;
import com.market.web.model.GenericModel;
import com.market.web.model.MarketList;

public abstract class GenericController<GM extends GenericManager<BO, Long>, GModel extends GenericModel<BO>, BO extends BaseObject> {
	protected GM manager;
	protected GModel model;

	protected final Log log = LogFactory.getLog(this.getClass());

	public GModel getModel() {
		return model;
	}

	protected void popMessage(String messageTitle, String message) {
		FacesContext.getCurrentInstance().addMessage(
				null,
				new FacesMessage(FacesMessage.SEVERITY_INFO, messageTitle,
						message));

	}

	protected ConfigurableNavigationHandler getNavHandler() {
		ConfigurableNavigationHandler handler = (ConfigurableNavigationHandler) FacesContext
				.getCurrentInstance().getApplication().getNavigationHandler();
		return handler;
	}

	/**
	 * This method load a domain object and all associated objects
	 * 
	 * @param bo
	 * @return
	 */

	public BO fetchOne(BO bo) {
		return this.manager.fetchOne(bo);
	}

	/**
	 * Reload current entity and all associated entity from database
	 */
	public void refreshCurrent() {
		BO current = this.manager.fetchOne(getModelCurrent());
		this.model.setCurrent(current);
	}

	/**
	 * This method load all entity bean from database into model. And load
	 * current entity and all it's associated entity in to model
	 */
	protected void loadModel() {
		List<BO> all = manager.fetchAll();
		if (all.size() > 0) {
			model.setList(all);
			this.refreshCurrent();
		}
		this.model.setIsInitialized(true);
	}

	public void initWithoutCheck() {
		loadModel();
		this.model.setIsInitialized(true);
	}

	public void init() {
		if (model.getIsInitialized() == false) {
			loadModel();
			this.model.setIsInitialized(true);
		}
	}

	/**
	 * Save current object in model to database; If current object is new
	 * object, current object will be added to model's list, and isNew property
	 * will be set to false;
	 */
	public BO saveCurrent() {
		BO current = model.getCurrent();
		current = manager.store(current);
		this.model.setCurrent(current); // If current is new object, have to
										// add
		// to
		// model to replace original one, because id
		// have changed
		this.popMessage("Save", "Save succeed");
		return current;
	}

	/**
	 * Delete current object from database, and remove it from model list
	 */
	public void deleteCurrent() {
		manager.delete(model.getCurrent());
		this.model.remove(this.model.getCurrent());
		this.manager.fetchOne(this.getModelCurrent());
		this.popMessage("Delete", "Delete succeed");
	}

	/**
	 * When the view Datatable select an object, then set the object into
	 * model's current;
	 * 
	 * @param event
	 */

	public void onRowSelect(SelectEvent event) {
		this.setModelCurrent((BO) event.getObject());
	}

	/**
	 * This is just a convenient way to set an object to Model's current
	 * property Before set to model, this method first refresh this object from
	 * database and load all associated objects;
	 * 
	 * @param current
	 */
	public void setModelCurrent(BO current) {
		if (current.getIsNew() == false) { // New object doesn't exist in
											// database
			current = (BO) this.manager.fetchOne(current);
		}
		this.model.setCurrent(current);
	}

	/**
	 * This is just a convenient way to get model's current property.
	 * 
	 * @return
	 */

	public BO getModelCurrent() {
		return (BO) this.model.getCurrent();
	}

	protected abstract void setModel(GModel model);

	/**
	 * Subclass must implement this method to set their correspondent manager
	 * For example, UserManager, ProductManager;
	 * 
	 * @param manager
	 */
	protected abstract void setManager(GM manager);

}
